Skip to content

refactor(ios): Migrate from PrivateSentrySDKOnly to SentrySDK.internal#6380

Open
alwx wants to merge 17 commits into
mainfrom
alwx/refactor/migrate-private-sentry-sdk-only
Open

refactor(ios): Migrate from PrivateSentrySDKOnly to SentrySDK.internal#6380
alwx wants to merge 17 commits into
mainfrom
alwx/refactor/migrate-private-sentry-sdk-only

Conversation

@alwx

@alwx alwx commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

📢 Type of change

  • Refactoring

📜 Description

Replace deprecated PrivateSentrySDKOnly SPI and SentrySwizzle.h macro usage in our iOS code with the new SentrySDK.internal Swift API exposed by sentry-cocoa 9.19.0, via a thin in-pod ObjC↔Swift bridge (RNSentryInternal.swift).

The bridge uses @_spi(Private) import Sentry to reach SPI-gated sub-APIs (performance.currentScreenFrames, replay.configure, envelope.{store,capture,deserialize}). Existing .m/.mm callers route through [RNSentryInternal …] via the auto-generated RNSentry-Swift.h. The performSelector:@selector(getReplayIntegration) workaround in captureReplayWithReturnValue is gone — SentrySDK.internal.replay.capture() returns Bool directly.

Notably, we do not use sentry-cocoa's new SentryObjC umbrella module (which the issue suggested) — that module isn't shipped via the Sentry CocoaPod in 9.19.0, only via SPM as a separate xcframework. The Swift bridge works equally on both consumption paths.

💡 Motivation and Context

Closes #6370. PrivateSentrySDKOnly is deprecated in sentry-cocoa 9.19.0 and slated for removal in the next major. Migrating now avoids future churn and drops the private header search path / performSelector: reliance.

💚 How did you test it?

  • yarn build, yarn test, yarn lint, yarn circularDepCheck — all green
  • xcodebuild for the RNSentry pod target (iphonesimulator) — builds clean
  • xcodebuild test for RNSentryCocoaTester — 158/158 native Cocoa tests pass

📝 Checklist

  • I added tests to verify changes
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • All tests passing
  • No breaking changes

🔮 Next steps

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


  • refactor(ios): Migrate from PrivateSentrySDKOnly to SentrySDK.internal by alwx in #6380
  • chore(deps): bump github/codeql-action/autobuild from 4.36.2 to 4.36.3 by dependabot in #6401
  • chore(deps): bump getsentry/craft/.github/workflows/changelog-preview.yml from 2.26.13 to 2.26.14 by dependabot in #6400
  • chore(deps): bump getsentry/craft from 2.26.13 to 2.26.14 by dependabot in #6402
  • chore(deps): bump github/codeql-action/analyze from 4.36.2 to 4.36.3 by dependabot in #6403
  • docs: Warn about possible Android new architecture build failures by antonis in #6408

🤖 This preview updates automatically when you update the PR.

@alwx

alwx commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@cursor review

Comment thread packages/core/ios/RNSentryInternal.swift
alwx and others added 3 commits July 2, 2026 09:24
Replace the deprecated PrivateSentrySDKOnly SPI and SentrySwizzle.h macro
with the new SentrySDK.internal Swift API exposed by sentry-cocoa 9.19.0,
via a thin in-pod ObjC<->Swift bridge (RNSentryInternal.swift). Imports
Sentry with @_spi(Private) for SPI-gated sub-APIs (performance, replay,
envelope). Drops the performSelector workaround around getReplayIntegration
since SentrySDK.internal.replay.capture() returns Bool directly.

Closes #6370

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move RNSentryStartTests, RNSentryStartFromFileTests, and RNSentryReplayOptionsTests
to SentrySDK.internal.{options, options(fromDictionary:), appStart.hybridSDKMode,
performance.framesTrackingHybridSDKMode}.

The ObjC RNSentryTests.m keeps PrivateSentrySDKOnly for now — the test target
is a separate consumer of the RNSentry static-lib pod and cannot see
RNSentry-Swift.h without modulemap surgery. Worth revisiting when sentry-cocoa
removes PrivateSentrySDKOnly in the next major.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`SentryInternalSwizzleApi` in sentry-cocoa has no platform gating, and
`SENTRY_HAS_UIKIT` (which guards the `[RNSentryRNSScreen swizzleViewDidAppear]`
call in RNSentry.mm) covers visionOS. The Swift bridge was gated to
`iOS/tvOS/macCatalyst` only, silently no-oping the swizzle on visionOS
where the deprecated `SentrySwizzle` macro used to work.

Also documents why `setCurrentScreen` remains a visionOS no-op: sentry-cocoa's
`SentryInternalScreenApi` is intentionally gated to iOS/tvOS, so the new API
surface itself doesn't expose the setter on visionOS.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@alwx alwx force-pushed the alwx/refactor/migrate-private-sentry-sdk-only branch from b850f46 to 330236b Compare July 2, 2026 07:25
@alwx alwx marked this pull request as ready for review July 2, 2026 07:26
@antonis antonis added the ready-to-merge Triggers the full CI test suite label Jul 2, 2026
@sentry

sentry Bot commented Jul 2, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
Sentry RN io.sentry.reactnative.sample 8.17.2 (97) Release

⚙️ sentry-react-native Build Distribution Settings

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

iOS (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3817.44 ms 1209.44 ms -2608.00 ms
Size 4.98 MiB 6.51 MiB 1.54 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
7a89652+dirty 3861.46 ms 1229.61 ms -2631.85 ms
5b7e8a7+dirty 3851.22 ms 1223.79 ms -2627.43 ms
5569641+dirty 3839.22 ms 1231.30 ms -2607.91 ms
9210ae6+dirty 3815.93 ms 1214.14 ms -2601.79 ms
88735e9+dirty 3837.02 ms 1214.40 ms -2622.62 ms
09a902f+dirty 3835.67 ms 1217.11 ms -2618.57 ms
5a23c47+dirty 3855.46 ms 1221.95 ms -2633.50 ms
94af3bd+dirty 3849.30 ms 1235.15 ms -2614.15 ms
5789645+dirty 3841.18 ms 1220.67 ms -2620.52 ms
1e5d96d+dirty 3851.45 ms 1212.05 ms -2639.41 ms

App size

Revision Plain With Sentry Diff
7a89652+dirty 5.15 MiB 6.70 MiB 1.55 MiB
5b7e8a7+dirty 5.15 MiB 6.70 MiB 1.54 MiB
5569641+dirty 5.15 MiB 6.67 MiB 1.51 MiB
9210ae6+dirty 5.15 MiB 6.68 MiB 1.53 MiB
88735e9+dirty 4.98 MiB 6.46 MiB 1.49 MiB
09a902f+dirty 4.98 MiB 6.46 MiB 1.49 MiB
5a23c47+dirty 4.98 MiB 6.46 MiB 1.49 MiB
94af3bd+dirty 5.15 MiB 6.69 MiB 1.53 MiB
5789645+dirty 4.98 MiB 6.50 MiB 1.52 MiB
1e5d96d+dirty 4.98 MiB 6.46 MiB 1.49 MiB

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

iOS (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3838.94 ms 1217.39 ms -2621.54 ms
Size 4.98 MiB 6.51 MiB 1.54 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
7a89652+dirty 3827.11 ms 1211.49 ms -2615.62 ms
5b7e8a7+dirty 3855.46 ms 1234.47 ms -2620.99 ms
5569641+dirty 3824.35 ms 1210.78 ms -2613.57 ms
9210ae6+dirty 3834.11 ms 1216.64 ms -2617.47 ms
88735e9+dirty 3837.80 ms 1216.39 ms -2621.41 ms
09a902f+dirty 3847.65 ms 1221.31 ms -2626.34 ms
5a23c47+dirty 3843.54 ms 1226.05 ms -2617.50 ms
94af3bd+dirty 3847.84 ms 1230.33 ms -2617.51 ms
40c9884+dirty 3826.11 ms 1217.04 ms -2609.07 ms
1e5d96d+dirty 3845.93 ms 1222.51 ms -2623.42 ms

App size

Revision Plain With Sentry Diff
7a89652+dirty 5.15 MiB 6.70 MiB 1.55 MiB
5b7e8a7+dirty 5.15 MiB 6.70 MiB 1.54 MiB
5569641+dirty 5.15 MiB 6.67 MiB 1.51 MiB
9210ae6+dirty 5.15 MiB 6.68 MiB 1.53 MiB
88735e9+dirty 4.98 MiB 6.46 MiB 1.49 MiB
09a902f+dirty 4.98 MiB 6.46 MiB 1.49 MiB
5a23c47+dirty 4.98 MiB 6.46 MiB 1.49 MiB
94af3bd+dirty 5.15 MiB 6.69 MiB 1.53 MiB
40c9884+dirty 4.98 MiB 6.51 MiB 1.53 MiB
1e5d96d+dirty 4.98 MiB 6.46 MiB 1.49 MiB

@antonis antonis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good. I think a conditional import like should fix the build issues 🤞

  #if __has_include(<RNSentry/RNSentry-Swift.h>)
  #import <RNSentry/RNSentry-Swift.h>
  #else
  #import "RNSentry-Swift.h"
  #endif

Comment thread packages/core/ios/RNSentryInternal.swift Outdated
`withUnsafePointer(to:)` only guarantees the pointer stays valid inside
the closure body. Capturing it in `keyPtr` and using it outside the
closure was undefined behaviour by contract (even though `static var`
storage happens to be stable in practice). Wrap the entire
`instanceMethod` call inside `withUnsafePointer` so the pointer is
always used within its documented lifetime; the `static var` backing
storage keeps the address itself stable across calls, so sentry-cocoa's
`oncePerClass` dedup keeps working.

Flagged by Cursor Bugbot.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread packages/core/ios/RNSentry.mm
alwx and others added 2 commits July 6, 2026 11:47
Handle both static-lib and framework-style Pod integrations by falling
back from `<RNSentry/RNSentry-Swift.h>` (frameworks path) to the local
`"RNSentry-Swift.h"` (static-lib path) via `__has_include`. Applied to
every `.m`/`.mm` in packages/core/ios that consumes the Swift bridge.

Suggested by @antonis.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@alwx alwx requested a review from antonis July 6, 2026 09:59
Two fixes flagged by Warden:

1. `RNSentryInternal.envelope(fromData:)` accepted a non-optional Swift
   `Data`. If the ObjC caller passed a nil `NSData*` (e.g. from a failed
   base64 decode of the envelope payload), the ObjC→Swift bridge would
   force-unwrap it and crash before the method body ran. Change the
   parameter to `Data?` with an internal `guard let` — matches the
   nil-tolerant behaviour of the deprecated
   `PrivateSentrySDKOnly.envelopeWithData:`.

2. Swift Cocoa tests accessed `SentrySDK.internal.*` with plain
   `import Sentry`. The `.internal` accessor is public today, but the
   sub-APIs the tests touch (`options`, `appStart`, `performance`) sit
   next to `@_spi(Private)`-gated siblings on the same struct. Add
   `@_spi(Private) import Sentry` to `RNSentryStartTests.swift`,
   `RNSentryStartFromFileTests.swift`, and `RNSentryReplayOptionsTests.swift`
   so they keep compiling if the surface is ever tightened.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread packages/core/ios/RNSentry.mm
alwx and others added 2 commits July 6, 2026 12:26
When `captureViewHierarchy` returned nil the ObjC code still constructed
an empty `NSMutableArray` and resolved with it. In JS, `[]` is truthy —
the caller would treat the failure as a successful (empty) attachment.
Bail out with `resolve(nil)` up front so the JS side sees the actual
failure signal.

Pre-existing since 2023; flagged by Seer while reviewing this PR.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread packages/core/ios/RNSentryInternal.swift
alwx and others added 2 commits July 6, 2026 14:16
Cover the bridge's own contract — nil-guard on `envelope(fromData:)`,
metadata accessors, options factory, hybrid-SDK-mode flags, and the
`swizzleRNSScreenViewDidAppear` early-return when RNSScreen is
unavailable. sentry-cocoa owns the underlying `SentrySDK.internal.*`
behaviour; we just assert the wrapper forwards correctly and honours
the documented nil / platform guards.

Registers the new file in the RNSentryCocoaTester project explicitly
(the target does not use file-system-synchronized groups yet).

Flagged by Warden.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adding Swift to the RNSentry pod means CocoaPods refuses to integrate it
against non-modular ObjC dependencies. On older React Native versions
(e.g. 0.71 with Hermes) the React-hermes pod does not define modules, so
`pod install` fails with:

  The Swift pod `RNSentry` depends upon `React-hermes`, which does not
  define modules.

Inject `use_modular_headers!` into the Podfile via the existing e2e patch
script, and document the same knob for downstream users in the CHANGELOG.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2cb3360. Configure here.

Comment thread dev-packages/e2e-tests/patch-scripts/rn.patch.podfile.js Outdated
Cherry-picked from #6410. Dependabot bumped `codeql-action/analyze` and
`codeql-action/autobuild` to v4.36.3 but left `init` on v4.36.2, so
`init` writes a config stamped 4.36.2 that `analyze` (4.36.3) rejects:

  Loaded a configuration file for version '4.36.2', but running version '4.36.3'

Pull this fix into the PR branch so CI stops failing while #6410 is
still open on main.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread packages/core/ios/RNSentryStart.m
Previous version unconditionally set `modularPatched = true` inside the
`use_modular_headers!` branch, so a Podfile without a
`prepare_react_native_project!` anchor would be reported as successfully
patched even though `content.replace` returned it unchanged.

Compare replace output against the input and log a warning instead when
the anchor is missing.

Flagged by Cursor Bugbot.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread packages/core/ios/RNSentryInternal.swift
The migration to `SentrySDK.internal.screen.setCurrent` drops the
current-screen breadcrumb enrichment on visionOS because sentry-cocoa's
new `SentryInternalScreenApi` is gated to iOS/tvOS only. Already
documented in a code comment; call it out for downstream users too.

Flagged by Seer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Android (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 424.23 ms 464.74 ms 40.51 ms
Size 49.74 MiB 55.09 MiB 5.34 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
d2eadf8+dirty 414.64 ms 454.56 ms 39.92 ms
4966363+dirty 400.04 ms 431.08 ms 31.04 ms
0307fa4+dirty 454.16 ms 518.35 ms 64.18 ms
038a6d7+dirty 524.82 ms 531.92 ms 7.10 ms
4e0b819+dirty 420.56 ms 470.08 ms 49.52 ms
1a5721e+dirty 424.07 ms 482.32 ms 58.25 ms
a0d8cf8+dirty 411.71 ms 467.57 ms 55.87 ms
9474ead+dirty 411.45 ms 446.80 ms 35.35 ms
ad66da3+dirty 468.46 ms 533.56 ms 65.10 ms
0b1b5e3+dirty 416.42 ms 470.58 ms 54.16 ms

App size

Revision Plain With Sentry Diff
d2eadf8+dirty 48.30 MiB 53.48 MiB 5.18 MiB
4966363+dirty 48.30 MiB 53.54 MiB 5.24 MiB
0307fa4+dirty 49.74 MiB 54.81 MiB 5.07 MiB
038a6d7+dirty 48.30 MiB 53.60 MiB 5.30 MiB
4e0b819+dirty 49.74 MiB 54.81 MiB 5.07 MiB
1a5721e+dirty 49.74 MiB 55.09 MiB 5.34 MiB
a0d8cf8+dirty 48.30 MiB 53.49 MiB 5.19 MiB
9474ead+dirty 48.30 MiB 53.61 MiB 5.30 MiB
ad66da3+dirty 48.30 MiB 53.49 MiB 5.19 MiB
0b1b5e3+dirty 48.30 MiB 53.60 MiB 5.29 MiB

`RNSentryInternal` is a Swift type compiled into the `RNSentry` pod
module, not the `Sentry` module. Without `@_spi(Private) import RNSentry`
the test target cannot resolve `RNSentryInternal.*` and fails to build.

Flagged by Warden.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Triggers the full CI test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate from PrivateSentrySDKOnly to [SentryObjCSDK internal]

2 participants